let uuid = require('uuid');

/**
 * takes an object, which must have the following methods:
 *
 * <pre>
 * draw(canvas, params) // params: {isActive: bool, mouseIsIn: bool}
 * </pre>
 *
 * and might have the following methods:
 *
 * <pre>
 * init(initConfig) // { 'id': string, 'game': game, 'componentContainer': componentContainer, 'canvas': canvas }
 * includesPoint(x, y)
 * active()
 * inactive()
 * move(deltaX, deltaY)
 * mouseDown()
 * mouseUp()
 * onXxx(sender, msg)
 * mouseIn()
 * mouseOut()
 * moveStart()
 * moveEnd()
 * mousePos()
 * </pre>
 */
class Component {
    constructor(o, id, zIndex) {
        this.id = id || uuid.v4();
        this.o = o;
        this.zIndex = zIndex;
        this.lastMoueDown = {
            x: -1,
            y: -1
        };
        this.isActive = false;
        this.mouseIsIn = false;
        this.moveStarted = false;
    }

    // init
    init(initConfig) {
        initConfig.id = this.id;
        if (this.o.init) {
            this.o.init(initConfig);
        }
    }

    // bool
    includesPoint(x, y) {
        if (this.o.includesPoint) {
            return this.o.includesPoint(x, y);
        } else if (this.o.hasOwnProperty('x')
            && this.o.hasOwnProperty('y')
            && this.o.hasOwnProperty('width')
            && this.o.hasOwnProperty('height')) {
            return this.o.x + this.o.width / 2 > x && this.o.x - this.o.width / 2 < x
                && this.o.y + this.o.height / 2 > y && this.o.y - this.o.height / 2 < y;
        } else {
            return false;
        }
    }

    // when mouse move in
    active() {
        this.isActive = true;
        if (this.o.active) {
            this.o.active();
        }
    }

    // when mouse move out
    inactive() {
        this.isActive = false;
        if (this.o.inactive) {
            this.o.inactive();
        }
    }

    // mouse in
    mouseIn() {
        if (this.mouseIsIn) return;
        this.mouseIsIn = true;
        if (this.o.mouseIn) {
            this.o.mouseIn();
        }
    }

    // mouse out
    mouseOut() {
        if (!this.mouseIsIn) return;
        this.mouseIsIn = false;
        if (this.o.mouseOut) {
            this.o.mouseOut();
        }
    }

    // when mouse moving
    // call this.o.move
    // args are calculated as delta
    move(x, y) {
        if (!this.moveStarted) {
            this.moveStarted = true;
            if (this.o.moveStart) {
                this.o.moveStart();
            }
        }
        let deltaX = x - this.lastMoueDown.x;
        let deltaY = y - this.lastMoueDown.y;
        this.lastMoueDown.x = x;
        this.lastMoueDown.y = y;
        if (this.o.move) {
            this.o.move(deltaX, deltaY);
        }
    }

    // whn mouse moving in the active component
    mousePos(x, y) {
        if (this.o.mousePos) {
            this.o.mousePos(x, y);
        }
    }

    // when mouse down
    mouseDown(x, y) {
        this.lastMoueDown.x = x;
        this.lastMoueDown.y = y;
        if (this.o.mouseDown) {
            this.o.mouseDown();
        }
    }

    // when mouse up
    mouseUp() {
        if (this.moveStarted) {
            this.moveStarted = false;
            if (this.o.moveEnd) {
                this.o.moveEnd();
            }
        }
        if (this.o.mouseUp) {
            this.o.mouseUp();
        }
    }

    // event
    on(route, sender, msg) {
        let method = 'on' + route.substring(0, 1).toUpperCase() + route.substring(1);
        if (this.o[method]) {
            this.o[method](sender, msg);
        }
    }

    // draw
    draw(canvas) {
        canvas.getContext('2d').restore();
        canvas.getContext('2d').save();
        canvas.getContext('2d').beginPath();
        this.o.draw(canvas, {
            isActive: this.isActive,
            mouseIsIn: this.mouseIsIn
        });
    }
}

module.exports = Component;
